/* ****************************************************************************
 * Copyright (c) TCC - 2009 IME - USP. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. 
 * A copy of the license can be found in http://msdn.microsoft.com/en-us/library/dd228915.aspx. 
 * By using this source code in any fashion, you are agreeing to be bound by the terms of the 
 * Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 * ***************************************************************************/

namespace SoundLibrary
{
    sealed class BitReserve
    {
        private const int BUFSIZE = 4096 * 8;
        private static readonly int BUFSIZE_MASK = BUFSIZE - 1;
        private int offset, totbit, buf_byte_idx;
        private int[] buf;

        #region Constructors

        internal BitReserve()
        {
            this.InitBlock();

            this.offset = 0;
            this.totbit = 0;
            this.buf_byte_idx = 0;
        }

        #endregion

        #region Public Methods

        public int Hsstell()
        {
            return this.totbit;
        }

        public int Hgetbits(int n)
        {
            this.totbit += n;

            int val = 0;

            int pos = this.buf_byte_idx;
            if (pos + n < BUFSIZE)
            {
                while (n-- > 0)
                {
                    val <<= 1;
                    val |= (this.buf[pos++] != 0) ? 1 : 0;
                }
            }
            else
            {
                while (n-- > 0)
                {
                    val <<= 1;
                    val |= (this.buf[pos] != 0) ? 1 : 0;
                    pos = (pos + 1) & BUFSIZE_MASK;
                }
            }

            this.buf_byte_idx = pos;
            return val;
        }

        public int Hget1bit()
        {
            this.totbit++;
            int val = this.buf[this.buf_byte_idx];
            this.buf_byte_idx = (this.buf_byte_idx + 1) & BUFSIZE_MASK;
            return val;
        }

        public void Hputbuf(int val)
        {
            int ofs = this.offset;
            this.buf[ofs++] = val & 0x80;
            this.buf[ofs++] = val & 0x40;
            this.buf[ofs++] = val & 0x20;
            this.buf[ofs++] = val & 0x10;
            this.buf[ofs++] = val & 0x08;
            this.buf[ofs++] = val & 0x04;
            this.buf[ofs++] = val & 0x02;
            this.buf[ofs++] = val & 0x01;

            if (ofs == BUFSIZE)
            {
                this.offset = 0;
            }
            else
            {
                this.offset = ofs;
            }
        }

        public void RewindNbits(int n)
        {
            this.totbit -= n;
            this.buf_byte_idx -= n;
            if (this.buf_byte_idx < 0)
            {
                this.buf_byte_idx += BUFSIZE;
            }
        }

        public void RewindNbytes(int n)
        {
            int bits = n << 3;
            this.totbit -= bits;
            this.buf_byte_idx -= bits;
            if (this.buf_byte_idx < 0)
            {
                this.buf_byte_idx += BUFSIZE;
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Inicia um bloco
        /// </summary>
        private void InitBlock()
        {
            this.buf = new int[BUFSIZE];
        }

        #endregion
    }
}